﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.DataVisualization.Charting;
using System.IO.IsolatedStorage;
using ContinuumSL.Classes;

// Continuum for Silverlight
// webpage : http://continuum4sl.codeplex.com
// live page : http://obeliskos.com/Continuum/Continuum.html
//
// This project is a Silverlight 5 port of one of my other Codeplex projects called Continuum. 
// The project is designed to manage personal finances via a means of a simulation-like environment. 
// Changes can be made which immediately get reflected in graphs projecting its effects over whatever timeframe you choose.
//
// Released under MS-PL liscense

namespace ContinuumSL
{
    /// <summary>
    /// The main page of Continuum for Silverlight.  Currently pretty much all ui is done here.
    /// <remarks>
    /// In the future, i may employ more ChildWindows for tasks other than password entry, 
    /// but for now it mostly fits and is actually closer to original intent of simulation environment.
    /// </remarks>
    /// </summary>
	public partial class MainPage : UserControl
	{
		#region Member Variables
		
		public const string BuildNumber = "1.2 SL";
        private string ContinuumPassword { get; set; }

		public static MainPage InstanceRef;
		private DateTime LastDate = DateTime.Now.Date;
		
		public static Projector projector = new Projector();
        public IsolatedStorageFile isostore = IsolatedStorageFile.GetUserStoreForApplication();

        public static string ConfigFile { get { return "universe.continuum"; } }
        public static string ConfigFileEnc { get { return "universe.continuumx"; } }

        private PasswordChildWindow pcw;

		#endregion

        #region Form Level Handlers

        public MainPage()
		{
			InitializeComponent();

            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
            MainPage.InstanceRef = this;

            ContinuumPassword = "";

            // Set some generic default time window since currently we do not store this as a setting
            dtpStartDate.SelectedDate = DateTime.Now.Date.AddMonths(-3);
            dtpEndDate.SelectedDate = DateTime.Now.Date.AddMonths(6);

            lblVersion.Text = BuildNumber;
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            // If no configuration exists, make user aware of only 'tutorial' provided so far
            if (!isostore.FileExists(ConfigFile) && !isostore.FileExists(ConfigFileEnc))
            {
                MessageBox.Show("If you are new, you might use 'Add Sample Data' in I/O tab", "Welcome to Continuum for Silverlight", MessageBoxButton.OK);
                return;
            }

            // Otherwise load continuum
            LoadContinuum();
        }

        #endregion

        #region Ribbon Button Handlers

        private void btnFullscreen_Click(object sender, System.Windows.RoutedEventArgs e)
		{
			Application.Current.Host.Content.IsFullScreen = !Application.Current.Host.Content.IsFullScreen;
		}

        private void btnZoomLeft_Click(object sender, RoutedEventArgs e)
        {
            if (tabRight.Width == 0) tabRight.Width = 560;
            else tabRight.Width = 0;
        }

        private void btnZoomRight_Click(object sender, RoutedEventArgs e)
        {
            tabRight.Width = dockMain.Width; 
        }

        private void btnSaveRibbon_Click(object sender, RoutedEventArgs e)
        {
            if (ContinuumPassword == "") SaveContinuum(ConfigFile);
            else SaveContinuum(ConfigFileEnc);
        }

        #endregion

        #region DateTrim Buttons

        // Range checking validated in the datepicker SelectedDateChanged events

        private void dtpEndDate_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
        {
            if (dtpStartDate.SelectedDate >= dtpEndDate.SelectedDate)
            {
                dtpStartDate.SelectedDate = dtpEndDate.SelectedDate.Value.AddMonths(-3);
            }

            RunProjection(false);
        }

        private void dtpStartDate_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
        {
            if (dtpStartDate.SelectedDate >= dtpEndDate.SelectedDate)
            {
                dtpEndDate.SelectedDate = dtpStartDate.SelectedDate.Value.AddMonths(3);
            }

            RunProjection(false);
        }

        private void btnStartYDec_Click(object sender, RoutedEventArgs e)
        {
            dtpStartDate.SelectedDate = dtpStartDate.SelectedDate.Value.AddYears(-1);
        }

        private void btnStartMDec_Click(object sender, RoutedEventArgs e)
        {
            dtpStartDate.SelectedDate = dtpStartDate.SelectedDate.Value.AddMonths(-1);
        }

        private void btnStartMInc_Click(object sender, RoutedEventArgs e)
        {
            dtpStartDate.SelectedDate = dtpStartDate.SelectedDate.Value.AddMonths(1);
        }

        private void btnStartYInc_Click(object sender, RoutedEventArgs e)
        {
            dtpStartDate.SelectedDate = dtpStartDate.SelectedDate.Value.AddYears(1);
        }

        private void btnEndYDec_Click(object sender, RoutedEventArgs e)
        {
            dtpEndDate.SelectedDate = dtpEndDate.SelectedDate.Value.AddYears(-1);
        }

        private void btnEndMDec_Click(object sender, RoutedEventArgs e)
        {
            dtpEndDate.SelectedDate = dtpEndDate.SelectedDate.Value.AddMonths(-1);
        }

        private void btnEndMInc_Click(object sender, RoutedEventArgs e)
        {
            dtpEndDate.SelectedDate = dtpEndDate.SelectedDate.Value.AddMonths(1);
        }

        private void btnEndYInc_Click(object sender, RoutedEventArgs e)
        {
            dtpEndDate.SelectedDate = dtpEndDate.SelectedDate.Value.AddYears(1);
        }

        #endregion

        #region Projection Management

        /// <summary>
        /// Used for forcing refresh of all lookup lists/dropdowns.  
        /// Usually called on startup and after changing data which would affect their values.
        /// </summary>
        private void RefreshLists()
        {
            ddlTransactionFunds.ItemsSource = null;
            ddlTransactionFunds.ItemsSource = projector.Funds;

            ddlBalanceFunds.ItemsSource = null;
            ddlBalanceFunds.ItemsSource = projector.Funds;

            ddlActivityFunds.ItemsSource = null;
            ddlActivityFunds.ItemsSource = projector.Funds;

            ddlHistoricalBalsFunds.ItemsSource = null;
            ddlHistoricalBalsFunds.ItemsSource = projector.Funds;

            // Select first item so details are loaded
            if (projector.Funds.Count > 0)
            {
                ddlTransactionFunds.SelectedIndex = 0;
                ddlBalanceFunds.SelectedIndex = 0;
                ddlActivityFunds.SelectedIndex = 0;
                ddlHistoricalBalsFunds.SelectedIndex = 0;
            }

            lstActors.ItemsSource = null;
            lstActors.ItemsSource = projector.Actors;
            if (projector.Actors.Count > 0) lstActors.SelectedIndex = 0;

            lstFunds.ItemsSource = null;
            lstFunds.ItemsSource = projector.Funds;
            if (projector.Funds.Count > 0) lstFunds.SelectedIndex = 0;

            ddlActorFundDest.ItemsSource = null;
            ddlActorFundDest.ItemsSource = projector.Funds;
            ddlActorFundDest.SelectedValuePath = "Name";

            ddlActorFundFeeder.ItemsSource = null;
            ddlActorFundFeeder.ItemsSource = projector.Funds;
            ddlActorFundFeeder.SelectedValuePath = "Name";

            ddlFundDisbursal.ItemsSource = null;
            ddlFundDisbursal.ItemsSource = projector.Funds;
            ddlFundDisbursal.SelectedValuePath = "Name";
            if (projector.Funds.Count > 0) ddlFundDisbursal.SelectedIndex = 0;

            ddlFundType.ItemsSource = null;
            ddlFundType.ItemsSource = Enum.GetNames(typeof(FundClassEnum));

            ddlActorPeriodicity.ItemsSource = null;
            ddlActorPeriodicity.ItemsSource = Enum.GetNames(typeof(ActorPeriodicityEnum));
        }

        private void RefreshStatistics()
        {
            Decimal NetValue = 0.0m, NetDebt = 0.0m, NetEquity = 0.0m;

            foreach (Fund fund in projector.Funds)
            {
                if (fund.FundClass == FundClassEnum.Debt || fund.FundClass == FundClassEnum.DebtEquity)
                {
                    if (!fund.IsBenchmark) NetDebt += fund.InterimBalance.Amount;

                    if (fund.FundClass == FundClassEnum.DebtEquity && !fund.IsBenchmark)
                    {
                        NetValue += (fund.InceptionBalance.Amount + fund.EquityOffset) - fund.InterimBalance.Amount;
                        NetEquity += (fund.InceptionBalance.Amount + fund.EquityOffset) - fund.InterimBalance.Amount;
                    }
                }
                else
                {
                    if (fund.FundClass == FundClassEnum.Savings && !fund.IsBenchmark)
                    {
                        NetValue += fund.InterimBalance.Amount;
                    }
                }
            }

            Decimal BeginningEquity = projector.CalcEquityInitial();
            lblEquityChange.Text = Decimal.Round(NetEquity - BeginningEquity, 2).ToString();

            lblNetEquity.Text = Decimal.Round(NetEquity, 2).ToString();
            lblProjectedValue.Text = Decimal.Round(NetValue, 2).ToString();
            lblProjectedDebt.Text = Decimal.Round(NetDebt, 2).ToString();
            lblNetDebt2.Text = Decimal.Round(NetDebt, 2).ToString();

            // Net Index : 0.00 - 1.00 means you owe more than you have equity+savings
            // Net Index : 1.00 - 2.00 as you approach 2.0 you approach zero debt
            // Net Index : 2.00 +      once you have no debt net index cannot be calculated
            if (NetDebt == 0) lblProjectedIndex.Text = "0.00";
            else
            {
                if (NetValue < NetDebt)
                {
                    lblProjectedIndex.Text = Decimal.Round(NetValue / NetDebt, 2).ToString();
                }
                else
                {
                    if (NetDebt > 0)
                    {
                        Decimal amt = 1.0m + (1.0m - (NetDebt / NetValue));
                        lblProjectedIndex.Text = Decimal.Round(amt, 2).ToString();
                    }
                    else
                    {
                        lblProjectedIndex.Text = "2.00+";
                    }
                }
            }

            // Calculate Overall DetlaShifts in fund classes (Savings, Debt)
            Decimal ValChange = projector.CalculateFundClassDelta(FundClassEnum.Savings, dtpStartDate.SelectedDate.Value);
            Decimal DebtChange = projector.CalculateFundClassDelta(FundClassEnum.Debt, dtpStartDate.SelectedDate.Value);
            DebtChange += projector.CalculateFundClassDelta(FundClassEnum.DebtEquity, dtpStartDate.SelectedDate.Value);
            Decimal NetValueChange = ValChange - DebtChange;

            lblSavingsChange.Text = ValChange.ToString();
            lblDebtChange.Text = DebtChange.ToString();

            lblNetValueChange.Text = NetValueChange.ToString();

            TimeSpan ts = dtpEndDate.SelectedDate.Value - dtpStartDate.SelectedDate.Value;
            Decimal NetValuePerDay = (Decimal)(NetValueChange / ts.Days);
            lblNetValDay.Text = Decimal.Round(NetValuePerDay, 2).ToString();

            Decimal WasteAmountPerDay = 0.0m;

            // Due to drastic effect on waste calculation which Reconciliations cause...
            // the only way to preseve the accuraccy/usefulness of the Waste calculation is to only include future (projected) transactions
            if (dtpEndDate.SelectedDate.Value > DateTime.Today.Date)
            {
                DateTime dtWasteBegins = dtpStartDate.SelectedDate.Value;
                if (dtWasteBegins < DateTime.Today.Date) dtWasteBegins = DateTime.Today.Date;
                TimeSpan wts = dtpEndDate.SelectedDate.Value - dtWasteBegins;

                Decimal WasteAmount = projector.CalculateFundClassDelta(FundClassEnum.SpendingAccount, dtWasteBegins);
                lblNetWaste.Text = WasteAmount.ToString();
                WasteAmountPerDay = (Decimal)(WasteAmount / wts.Days);
                lblNetWasteDay.Text = Decimal.Round(WasteAmountPerDay, 2).ToString();
            }
            else
            {
                lblNetWaste.Text = "N/A";
                lblNetWasteDay.Text = "N/A";
            }

            // Calculate Interest
            Decimal InterestAmount = projector.CalculateInterestOverhead(dtpStartDate.SelectedDate.Value);
            Decimal InterestAmountDay = InterestAmount / ts.Days;
            lblNetInterest.Text = InterestAmount.ToString();
            lblNetInterestDay.Text = Decimal.Round(InterestAmountDay, 2).ToString();

            Decimal InterestMonth = Decimal.Round((Decimal)(InterestAmountDay * 30.5m), 2);
            Decimal InterestYear = Decimal.Round((Decimal)(InterestAmountDay * 365), 2);

            Decimal NetVolumePerDay = WasteAmountPerDay + NetValuePerDay + InterestAmountDay;
            lblTotalVolumeDay.Text = Decimal.Round(NetVolumePerDay, 2).ToString();
            lblTotalVolumeMonth.Text = Decimal.Round(NetVolumePerDay * 30.5m, 2).ToString();
            lblTotalVolumeYear.Text = Decimal.Round(NetVolumePerDay * 365, 2).ToString();

            RenderDistributionGraph(Decimal.Round(NetValuePerDay, 2),
                                    Decimal.Round(WasteAmountPerDay, 2),
                                    Decimal.Round(InterestAmountDay, 2));

            Decimal TotalSavings = 0.0m;
            foreach (Fund f in projector.Funds)
            {
                if (f.FundClass == FundClassEnum.Savings && !f.IsBenchmark)
                {
                    TotalSavings += f.InterimBalance.Amount;
                }
            }
            lblNetSavings.Text = Decimal.Round(TotalSavings, 2).ToString();

            lblStatFundCount.Text = projector.Funds.Count.ToString();
            lblStatActorCount.Text = projector.Actors.Count.ToString();
            lblStatActivityCount.Text = projector.Activities.Count.ToString();
        }

        private void RefreshTransactionGrid()
        {
            dgTransactions.ItemsSource = null;

            if (projector.Funds.Count == 0) return;

            Fund f = (Fund)ddlTransactionFunds.SelectedItem;
            if (f == null) return;

            // Grid was throwing weird exceptions on resizing of columns
            // So im flattening the model by casting as balances
            List<Balance> balances = new List<Balance>();
            foreach (Transaction trans in f.VolatileTransactions)
            {
                balances.Add((Balance)trans);
            }

            dgTransactions.ItemsSource = balances;
        }

        private void RefreshBalanceGrid()
        {
            dgBalances.ItemsSource = null;

            if (projector.Funds.Count == 0) return;

            Fund f = (Fund)ddlBalanceFunds.SelectedItem;
            if (f == null) return;

            DateTime StartDate = dtpStartDate.SelectedDate.Value, EndDate = dtpEndDate.SelectedDate.Value;

            dgBalances.ItemsSource = f.CombinedBalances(true);
        }

        private void RefreshActivityTab()
        {
            dgActivities.ItemsSource = null;

            if (projector.Activities.Count == 0) return;

            if (ddlActivityFunds.SelectedIndex > 0)
            {
                List<Activity> filtered = projector.ActivitiesByFund((Fund)ddlActivityFunds.SelectedItem);
                dgActivities.ItemsSource = filtered;
            }
            else
            {
                dgActivities.ItemsSource = projector.Activities;
            }
        }

        private void RunProjection(bool DoRefreshLists)
        {
            //if (IsLoading) return;

            if (DoRefreshLists) RefreshLists();

            projector.ResetVolatile();

            // Need to decouple Projection Window from GraphWindow
            // I was finding actors were not firing outside my time window
            // So i need to Render the Universe from the earliest fund's Inception Date
            // but only RenderGraph for the window chosen
            DateTime dtUniverseInception = dtpStartDate.SelectedDate ?? DateTime.Now.Date;
            foreach (Fund fund in projector.Funds)
            {
                if (fund.InceptionBalance.BalanceDate < dtUniverseInception && fund.InceptionBalance.BalanceDate != DateTime.MinValue)
                    dtUniverseInception = fund.InceptionBalance.BalanceDate;
            }

            projector.RenderUniverse(dtUniverseInception, dtpEndDate.SelectedDate ?? DateTime.Now);

            DateTime StartDate = dtpStartDate.SelectedDate.Value, EndDate = dtpEndDate.SelectedDate.Value;

            RenderActivityGraph(StartDate, EndDate);
            RefreshTransactionGrid();
            RefreshBalanceGrid();
            RefreshActivityTab();
            RefreshStatistics();
            RenderFundDisbursalGraph();
            RenderStackedIncomeGraph(StartDate, EndDate);
            RenderStackedDebtGraph(StartDate, EndDate);
        }

        #endregion

        #region Graphing Functions

        private void RenderDistributionGraph(Decimal NetValue, Decimal NetWaste, Decimal NetInterest)
        {
            List<Balance> dist = new List<Balance>();

            dist.Add(new Balance(NetValue, DateTime.Now.Date, "Net Value"));
            dist.Add(new Balance(NetWaste, DateTime.Now.Date, "Net Waste"));
            dist.Add(new Balance(NetInterest, DateTime.Now.Date, "Net Interest"));

            chartDistribution.DataContext = dist;
        }

        private void RenderFundDisbursalGraph()
        {
            if (projector.Funds.Count == 0) return;
            Fund f = (Fund) ddlFundDisbursal.SelectedItem;
            if (f == null) return;

            bool IsDebt = (f.FundClass == FundClassEnum.Debt || f.FundClass == FundClassEnum.DebtEquity);

            if (IsDebt)
            {
                if (chkFundDisburseReversed.IsChecked.Value) chartFundDisbursal.Title = "Projected Debt Increases";
                else chartFundDisbursal.Title = "Projected Debt Reductions";
            }
            else
            {
                if (chkFundDisburseReversed.IsChecked.Value) chartFundDisbursal.Title = "Projected Fund Inflows";
                else chartFundDisbursal.Title = "Projected Fund Disbursals";
            }

            Dictionary<string, Transaction> GroupedTransactions = f.GroupTransactions(dtpStartDate.SelectedDate.Value);
            List<Balance> OffsetTrans = new List<Balance>();
            
            foreach (KeyValuePair<string, Transaction> kvp in GroupedTransactions)
            {
                // Only add the 'disbursal Actors' (Actors whos cumulative effect was to lower balance)
                if ((chkFundDisburseReversed.IsChecked.Value ? kvp.Value.Amount * -1 : kvp.Value.Amount) < 0)
                {
                    Transaction t = kvp.Value;

                    // Odd repack into bindable collection format (List<Balance>)
                    Balance b = new Balance();
                    b.Amount = Math.Abs(t.Amount) * (IsDebt ? -1 : 1);
                    b.Description = kvp.Key;

                    OffsetTrans.Add(b);
                }
            }

            chartFundDisbursal.DataContext = OffsetTrans;
        }

        private void RenderStackedIncomeGraph(DateTime StartDate, DateTime EndDate)
        {
            List<Fund> ValueFunds = projector.GetValueFunds();
            List<Decimal> StackedTotals = new List<decimal>();
            List<Balance>[] ballist = new List<Balance>[ValueFunds.Count];

            chartStackedValue.Series.Clear();
            chartStackedValue.Title = "Stacked Income Projection for " + StartDate.ToShortDateString() + " - " + EndDate.ToShortDateString();

            for (int i = 0; i < ValueFunds.Count; i++)
            {
                ballist[i] = new List<Balance>();
            }

            DateTime CurrDate = DateTime.MinValue;
            string tooltip = "";

            foreach (Activity activity in projector.Activities)
            {
                // seek up to renderwindow
                if (activity.ActivityDate < StartDate) continue;
                // only process unique dates
                if (activity.ActivityDate == CurrDate) continue;

                CurrDate = activity.ActivityDate;
                Double RunningTotal = 0.0;
                Double CurrAmount = 0.0;
                for (int fundidx = 0; fundidx < ValueFunds.Count; fundidx++)
                {

                    if (ValueFunds[fundidx].FundClass == FundClassEnum.DebtEquity)
                    {
                        CurrAmount = (double)(ValueFunds[fundidx].InceptionBalance.Amount - ValueFunds[fundidx].CalculateBalance(CurrDate).Amount);
                        RunningTotal += CurrAmount;
                        tooltip = ValueFunds[fundidx].Name + " (Equity)\n" + activity.ActivityDate.ToShortDateString() + "\n" + CurrAmount.ToString();
                    }
                    else
                    {
                        CurrAmount = (double)ValueFunds[fundidx].CalculateBalance(CurrDate).Amount;
                        RunningTotal += CurrAmount;
                        tooltip = ValueFunds[fundidx].Name + "\n" + activity.ActivityDate.ToShortDateString() + "\n" + CurrAmount.ToString();
                    }

                    ballist[fundidx].Add(new Balance((Decimal) RunningTotal, CurrDate, tooltip));
                }
            }

            for (int fundidx = 0; fundidx < ValueFunds.Count; fundidx++)
            {
                Fund f = ValueFunds[fundidx];

                AreaSeries aseries = new AreaSeries();
                aseries.DependentValuePath = "Amount";
                aseries.IndependentValuePath = "BalanceDate";
                aseries.Title = f.Name;
                aseries.ItemsSource = ballist[fundidx];
                chartStackedValue.Series.Add(aseries);
            }
        }

        private void RenderStackedDebtGraph(DateTime StartDate, DateTime EndDate)
        {
            List<Fund> DebtFunds = projector.GetDebtFunds();
            List<Decimal> StackedTotals = new List<decimal>();
            List<Balance>[] ballist = new List<Balance>[DebtFunds.Count];

            chartStackedDebt.Series.Clear();
            chartStackedDebt.Title = "Stacked Debt Graph for " + StartDate.ToShortDateString() + " - " + EndDate.ToShortDateString();

            for (int i = 0; i < DebtFunds.Count; i++)
            {
                ballist[i] = new List<Balance>();
            }

            DateTime CurrDate = DateTime.MinValue;
            string tooltip = "";

            foreach (Activity activity in projector.Activities)
            {
                // seek up to renderwindow
                if (activity.ActivityDate < StartDate) continue;
                // only process unique dates
                if (activity.ActivityDate == CurrDate) continue;

                CurrDate = activity.ActivityDate;
                Double RunningTotal = 0.0;
                Double CurrAmount = 0.0;
                for (int fundidx = 0; fundidx < DebtFunds.Count; fundidx++)
                {
                    CurrAmount = (double)DebtFunds[fundidx].CalculateBalance(CurrDate).Amount;
                    RunningTotal += CurrAmount;

                    tooltip = DebtFunds[fundidx].Name + "\n" + activity.ActivityDate.ToShortDateString() + "\n" + CurrAmount.ToString();
                    ballist[fundidx].Add(new Balance((Decimal)RunningTotal, CurrDate, tooltip));
                }
            }

            for (int fundidx = 0; fundidx < DebtFunds.Count; fundidx++)
            {
                Fund f = DebtFunds[fundidx];

                //curve = myPane.AddCurve(f.Name, pplist[fundidx], c, st);
                //curve.Line.Width = 1.5F;
                AreaSeries aseries = new AreaSeries();
                aseries.DependentValuePath = "Amount";
                aseries.IndependentValuePath = "BalanceDate";

                if (!chkStackedDebtRelative.IsChecked.Value)
                {
                    LinearAxis la = new LinearAxis();
                    la.Title = "";
                    la.Orientation = AxisOrientation.Y;
                    la.Minimum = 0.0;
                    aseries.DependentRangeAxis = la;
                }
                aseries.Title = f.Name;
                aseries.ItemsSource = ballist[fundidx];
                chartStackedDebt.Series.Add(aseries);

            }

            //chartStackedDebt.Axes
            //zedGraphStackedDebt.AxisChange();
            //zedGraphStackedDebt.Refresh();
        }

        private void RenderActivityGraph(DateTime StartDate, DateTime EndDate)
		{
			TimeSpan ts = EndDate - StartDate;
			
            string tooltip;

            chartFundActivity.Series.Clear();
            chartFundActivity.Title = "Activity Graph for " + StartDate.ToShortDateString() + " - " + EndDate.ToShortDateString();

            // Re-aligned code around activities instead of TriggerDates
            foreach (Fund fund in projector.Funds)
            {
                if (fund.IsHidden) continue;

				List<Balance> bals = new List<Balance>();

                if (fund.InceptionBalance.BalanceDate < EndDate)
                {
                    if (fund.InceptionBalance.BalanceDate >= StartDate)
                    {
                        bals.Add(fund.InceptionBalance);
                    }
                }

                foreach (Activity activity in projector.ActivitiesByFund(fund))
                {
                    if (activity.ActivityDate >= StartDate)  // New filtering to decouple projection window from render window
                    {
                        Balance FundBalance = null;

                        if (activity.ActivityType == ActivityTypeEnum.Actor)
                        {
                            if (activity.AffectingActor.ActorFundPrimary == fund.Name)
                                FundBalance = activity.RenderedBalancePrimary;
                            if (activity.AffectingActor.ActorFundSecondary == fund.Name)
                                FundBalance = activity.RenderedBalanceSecondary;
                        }

                        if (activity.ActivityType == ActivityTypeEnum.FundReconcile &&
                            activity.AffectingFund == fund)
                            FundBalance = activity.RenderedBalancePrimary;

                        // If the fund is referenced by this activity, then add balance to plot
                        if (FundBalance != null)
                        {
							bals.Add(FundBalance);
                            tooltip = FundBalance.BalanceDate.ToShortDateString() + "\n$" + FundBalance.Amount.ToString() + "\n" + FundBalance.Description;
                        }
                    }
                }

                if (bals.Count == 1)
                {
                    Balance endbal = new Balance();
                    endbal.Amount = fund.InceptionBalance.Amount;
                    endbal.BalanceDate = EndDate;
                    endbal.Description = "Projection End bal";

                    bals.Add(endbal);
                }
                LineSeries ls2 = new LineSeries();
                ls2.DependentValuePath = "Amount";
                ls2.IndependentValuePath = "BalanceDate";
                ls2.Title = fund.Name;
                ls2.ItemsSource = bals;
                chartFundActivity.Series.Add(ls2);
            }
		}

        #endregion

        #region IO / Sample Data Tab Handlers

        private void btnAddSampleData_Click(object sender, System.Windows.RoutedEventArgs e)
		{
			// Add Sample Checking, Income Actor, and Mortgage Account, and Mortgage Actor to start
			
			// Add Sample Checking
			Fund fund = new Fund(
				new Balance(1000.0m, DateTime.Now.Date.AddMonths(-4), "Checking Inception"),
				"Sample Checking", FundClassEnum.SpendingAccount, false);
			
			MainPage.projector.Funds.Add(fund);
			
			// Add income actor, 1500 bi-weekly
			Actor actor = new Actor("Paycheck Deposit", "Sample Checking", 
				ActorPeriodicityEnum.Weekly, 2, DateTime.Now.Date.AddMonths(-3), 1500.0m);
			
			MainPage.projector.Actors.Add(actor);
			
			// Add Mortgage Account
			Fund mort = new Fund(
				new Balance(140000.0m, DateTime.Now.Date.AddMonths(-2), "Loan Inception"),
				"Sample Mortgage", FundClassEnum.DebtEquity, false);
			MainPage.projector.Funds.Add(mort);
			
			// Add Mortgage Payment Actor
			Actor mortactor = new Actor("Sample Mortgage LoanPayment", "Sample Mortgage", "Sample Checking",
				ActorPeriodicityEnum.Monthly, 1, DateTime.Now.Date.AddMonths(-1), 800);
			mortactor.IsLoanPayment = true;
			mortactor.InterestRate = 5.0m;
			mortactor.LoanDurationInYears = 30;
			
			MainPage.projector.Actors.Add(mortactor);
			
            // Add Savings Fund
            Fund savings = new Fund(
                new Balance(1000.0m, DateTime.Now.Date.AddMonths(-1), "Savings Inception"),
                "Sample Savings", FundClassEnum.Savings, false);
            MainPage.projector.Funds.Add(savings);

            // Add Sample Savings Plan  (Checking -> Savings)
            Actor savactor = new Actor("Sample Savings Plan", "Sample Savings", "Sample Checking", ActorPeriodicityEnum.Monthly, 1, DateTime.Now.Date, 150.0m);
            MainPage.projector.Actors.Add(savactor);

            RunProjection(true);
		}

        private void btnLoad_Click(object sender, RoutedEventArgs e)
        {
            LoadContinuum();
        }

        private void btnSave_Click(object sender, RoutedEventArgs e)
        {
            if (ContinuumPassword == "") SaveContinuum(ConfigFile);
            else SaveContinuum(ConfigFileEnc);
        }

        private void btnClearData_Click(object sender, RoutedEventArgs e)
        {
            MessageBoxResult mbr = MessageBox.Show("This will delete all data, are you sure?", "Erase Configuration", MessageBoxButton.OKCancel);
            if (mbr != MessageBoxResult.OK) return;

            if (isostore.FileExists(ConfigFile)) isostore.DeleteFile(ConfigFile);

            if (isostore.FileExists(ConfigFileEnc))
            {
                // if trying to delete encrypted data, make sure password is correct by trying to open it
                // if incorrect an exception should occur
                if (ContinuumPassword != "")
                {
                    // if you forgot your password you can either uninstall/reinstall or use this password
                    // to allow you to delete old data (cant read datafile but you could delete this way)
                    if (ContinuumPassword != "forcedelete")
                    {
                        try
                        {
                            projector = Projector.LoadUniverse(ConfigFileEnc, ContinuumPassword);
                        }
                        catch (System.Security.Cryptography.CryptographicException)
                        {
                            MessageBox.Show("Your current password does not match", "Unable to delete continuum", MessageBoxButton.OK);
                            return;
                        }
                    }

                    isostore.DeleteFile(ConfigFileEnc);
                    projector = new Projector();
                    RunProjection(true);
                }
                else
                {
                    MessageBox.Show("An encrypted version still exists, but session password has not been set. Reload with password to clear that version", "You must reload with password", MessageBoxButton.OK);
                }
            }
        }

        private void btnExport_Click(object sender, RoutedEventArgs e)
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Filter = "Continuum Files | *.continuum";
            sfd.DefaultExt = "continuum";

            bool? result = sfd.ShowDialog();

            if (!result.Value) return;

            using (System.IO.Stream fileStream = sfd.OpenFile())
            {
                projector.SaveUniverse(fileStream);
            }
        }

        private void btnImport_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "Continuum Files | *.continuum";

            bool? result = ofd.ShowDialog();

            if (!result.Value) return;

            using (System.IO.Stream fileStream = ofd.File.OpenRead())
            {
                projector = Projector.LoadUniverse(fileStream);

                RunProjection(true);
            }

        }

        #endregion

        #region Create/Load/Save Continuum

        private void enterPassword_SubmitClicked(object sender, EventArgs e)
        {
            ContinuumPassword = pcw.Password;

            // Not the most elegant way to infer filename but the only time this is called is for this filename
            // possibly tramp filename through childwindow public property in future?
            try
            {
                projector = Projector.LoadUniverse(ConfigFileEnc, ContinuumPassword);
            }
            catch (System.Security.Cryptography.CryptographicException)
            {
                MessageBox.Show("Invalid password or file format, reload to try again.", "Unable to load continuum", MessageBoxButton.OK);
                return;
            }

            RunProjection(true);
        }

        private bool LoadContinuum()
        {
            string filename = "";

            if (ContinuumPassword == "")
            {
                if (isostore.FileExists(ConfigFile)) filename = ConfigFile;
                else
                {
                    if (isostore.FileExists(ConfigFileEnc))
                    {
                        filename = ConfigFileEnc;

                        pcw = new PasswordChildWindow();
                        pcw.SetupEnterPassword();
                        pcw.SubmitClicked += new EventHandler(enterPassword_SubmitClicked);
                        pcw.Show();  // cant seem to force focus on first load

                        // async so just return true, any exceptions should show error and render empty continuum
                        return true;
                    }
                    else return false;
                }
            }

            projector = Projector.LoadUniverse(filename);

            RunProjection(true);

            return true;
        }

        private void SaveContinuum(string Filename)
        {
            if (Filename == ConfigFile) projector.SaveUniverse(Filename);
            else projector.SaveUniverse(Filename, ContinuumPassword);
        }

        private void passwordChange_SubmitClicked(object sender, EventArgs e)
        {
            ContinuumPassword = pcw.Password;

            if (ContinuumPassword == "")
            {
                // must be wanting to remove encryption, so save unenc and del any enc version
                projector.SaveUniverse(ConfigFile);
                if (isostore.FileExists(ConfigFileEnc)) isostore.DeleteFile(ConfigFileEnc);
            }
            else
            {
                // Save encrypted version, then delete uncrypted version
                projector.SaveUniverse(ConfigFileEnc, ContinuumPassword);
                if (isostore.FileExists(ConfigFile)) isostore.DeleteFile(ConfigFile);
            }
        }

        private void btnEncrypt_Click(object sender, RoutedEventArgs e)
        {
            if (MessageBox.Show("Are you sure you want to change encryption setting?", "Confirm Encryption/Decryption", MessageBoxButton.OKCancel) != MessageBoxResult.OK) return;

            pcw = new PasswordChildWindow();
            pcw.SetupSetPassword();
            pcw.SubmitClicked += new EventHandler(passwordChange_SubmitClicked);

            pcw.Show();
        }

        #endregion

        #region Misc RightPane Handlers

        private void ddlTransactionFunds_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            RefreshTransactionGrid();
        }


        private void ddlBalanceFunds_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            RefreshBalanceGrid();
        }

        #endregion

        #region Funds Tab Handlers

        private void LockFundDetails()
        {
            txtFundName.IsEnabled = false;
            dtpFundInception.IsEnabled = false;
            txtFundInceptionAmount.IsEnabled = false;
            txtFundEquityOffset.IsEnabled = false;
            ddlFundType.IsEnabled = false;
            chkFundBenchmark.IsEnabled = false;
            chkFundHide.IsEnabled = false;
        }

        private void UnlockFundDetails()
        {
            txtFundName.IsEnabled = true;
            dtpFundInception.IsEnabled = true;
            txtFundInceptionAmount.IsEnabled = true;
            txtFundEquityOffset.IsEnabled = true;
            ddlFundType.IsEnabled = true;
            chkFundBenchmark.IsEnabled = true;
            chkFundHide.IsEnabled = true;
        }

        private void ClearFundDetails()
        {
            txtFundName.Text = "";
            dtpFundInception.SelectedDate = DateTime.Now.Date;
            txtFundInceptionAmount.Text = "";
            txtFundEquityOffset.Text = "";
            ddlFundType.SelectedItem = null;
            chkFundBenchmark.IsChecked = false;
            chkFundHide.IsChecked = false;
        }

        /// <summary>
        /// Used for validating details.
        /// </summary>
        /// <remarks>Possibly refactor in the future for more native Silverlight validations</remarks>
        /// <returns></returns>
        private string ValidateFundDetails()
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            if (txtFundName.Text == "") sb.Append("Fund Name is required\r\n");
            if (dtpFundInception.SelectedDate == null) sb.Append("Fund inception date is required\r\n");
            if (txtFundInceptionAmount.Text == "") sb.Append("Inception amount is required, enter 0 if none\r\n");
            else
            {
                Decimal d;

                if (!Decimal.TryParse(txtFundInceptionAmount.Text, out d)) sb.Append("Inception amount is not a valid amount\r\n");
            }
            if (txtFundEquityOffset.Text != "")
            {
                Decimal d;

                if (!Decimal.TryParse(txtFundEquityOffset.Text, out d)) sb.Append("Equity offset is not a valid amount\r\n");
            }
            if (ddlFundType.SelectedItem == null) sb.Append("You must select a Fund Type\r\n");

            return sb.ToString();
        }

        private void lstFunds_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (lstFunds.SelectedItem == null) return;

            Fund f = (Fund)lstFunds.SelectedItem;

            txtFundName.Text = f.Name;
            dtpFundInception.SelectedDate = f.InceptionBalance.BalanceDate;
            txtFundInceptionAmount.Text = f.InceptionBalance.Amount.ToString();
            txtFundEquityOffset.Text = "";

            ddlFundType.SelectedItem = f.FundClass.ToString();

            chkFundBenchmark.IsChecked = f.IsBenchmark;
            chkFundHide.IsChecked = f.IsHidden;

            LockFundDetails();
        }

        private void btnFundEdit_Click(object sender, RoutedEventArgs e)
        {
            UnlockFundDetails();
            txtFundName.IsEnabled = false;
        }

        private void btnFundNew_Click(object sender, RoutedEventArgs e)
        {
            ClearFundDetails();
            UnlockFundDetails();
        }

        private void btnFundSave_Click(object sender, RoutedEventArgs e)
        {
            string errors = ValidateFundDetails();

            if (errors != "")
            {
                MessageBox.Show(errors, "Validation errors need to be fixed", MessageBoxButton.OK);
                return;
            }

            Fund fund;

            if (txtFundName.IsEnabled)
            {
                fund = new Fund();
                fund.Name = txtFundName.Text;
            }
            else fund = (Fund) lstFunds.SelectedItem;

            Balance incep = new Balance();
            incep.BalanceDate = dtpFundInception.SelectedDate.Value;
            incep.Amount = Decimal.Parse(txtFundInceptionAmount.Text);
            incep.Description = "Fund Inception";
            fund.InceptionBalance = incep;

            if (txtFundEquityOffset.Text == "") fund.EquityOffset = 0.0m;
            else fund.EquityOffset = Decimal.Parse(txtFundEquityOffset.Text);

            fund.FundClass = (FundClassEnum) Enum.Parse(typeof(FundClassEnum), ddlFundType.SelectedItem.ToString(), false);

            fund.IsBenchmark = chkFundBenchmark.IsChecked.Value;
            fund.IsHidden = chkFundHide.IsChecked.Value;

            if (txtFundName.IsEnabled)
            {
                projector.Funds.Add(fund);
                RunProjection(true);
            }
            else RunProjection(false);

            LockFundDetails();
        }

        private void btnFundDelete_Click(object sender, RoutedEventArgs e)
        {
            if (lstFunds.SelectedItem == null) return;

            Fund fund = (Fund)lstFunds.SelectedItem;

            string ActorNames = "";
            foreach (Actor actor in projector.Actors)
            {
                if (actor.ActorFundPrimary == fund.Name || actor.ActorFundSecondary == fund.Name)
                    ActorNames += actor.ActorName + "\r\n";
            }
            if (ActorNames.Length > 0)
            {
                MessageBox.Show("You must fix the following Actors referencing this fund before the fund can be deleted : \r\n\r\n" + ActorNames, "Unable to delete Referenced Fund", MessageBoxButton.OK);
                return;
            }

            if (MessageBox.Show("Are you sure you want to delete the fund '" + lstFunds.SelectedItem.ToString() + "'?", "Confirm fund deletion", MessageBoxButton.OKCancel) != MessageBoxResult.OK) return;

            projector.Funds.Remove(fund);

            RunProjection(true);

        }

        #endregion

        #region Actors Tab Management

        private void LockActorDetails()
        {
            txtActorName.IsEnabled = false;
            chkActorDisabled.IsEnabled = false;
            ddlActorFundDest.IsEnabled = false;
            ddlActorFundFeeder.IsEnabled = false;
            chkActorHasFeeder.IsEnabled = false;
            ddlActorPeriodicity.IsEnabled = false;
            txtActorPeriodicityUnits.IsEnabled = false;
            dtpActorTriggerStart.IsEnabled = false;
            chkActorStopTrigger.IsEnabled = false;
            dtpActorTriggerStop.IsEnabled = false;
            txtActorTriggerAmount.IsEnabled = false;
            chkActorIsLoanPayment.IsEnabled = false;
            chkActorIsInterestAccrual.IsEnabled = false;
            txtActorAccrualRate.IsEnabled = false;
        }

        private void UnlockActorDetails()
        {
            txtActorName.IsEnabled = true;
            chkActorDisabled.IsEnabled = true;
            ddlActorFundDest.IsEnabled = true;
            ddlActorFundFeeder.IsEnabled = true;
            chkActorHasFeeder.IsEnabled = true;
            ddlActorPeriodicity.IsEnabled = true;
            txtActorPeriodicityUnits.IsEnabled = true;
            dtpActorTriggerStart.IsEnabled = true;
            chkActorStopTrigger.IsEnabled = true;
            dtpActorTriggerStop.IsEnabled = true;
            txtActorTriggerAmount.IsEnabled = true;
            chkActorIsLoanPayment.IsEnabled = true;
            chkActorIsInterestAccrual.IsEnabled = true;
            txtActorAccrualRate.IsEnabled = true;
        }

        private void ClearActorDetails()
        {
            txtActorName.Text = "";
            chkActorDisabled.IsChecked = false;
            ddlActorFundDest.SelectedItem = null;
            ddlActorFundFeeder.SelectedItem = null;
            chkActorHasFeeder.IsChecked = false;
            ddlActorPeriodicity.SelectedItem = null;
            txtActorPeriodicityUnits.Text = "1";
            dtpActorTriggerStart.SelectedDate = DateTime.Now.Date;
            chkActorStopTrigger.IsChecked = false;
            dtpActorTriggerStop.SelectedDate = DateTime.Now.Date;
            txtActorTriggerAmount.Text = "";
            chkActorIsLoanPayment.IsChecked = false;
            chkActorIsInterestAccrual.IsChecked = false;
            txtActorAccrualRate.Text = "";
        }

        private string ValidateActorDetails()
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            
            if (txtActorName.Text == "") sb.Append("Actor Name cannot be blank\r\n");
            if (ddlActorFundDest.SelectedItem == null) sb.Append("You must specify a Destination Fund\r\n");
            if (ddlActorPeriodicity.SelectedItem == null) sb.Append("You must specify an Actor Periodicity\r\n");
            if (txtActorPeriodicityUnits.Text == "") sb.Append("You must specify periodicty units\r\n");
            else
            {
                int i;
                if (!int.TryParse(txtActorPeriodicityUnits.Text, out i)) sb.Append("Periodicity units is not a valid integer\r\n");
            }
            if (dtpActorTriggerStart.SelectedDate == null) sb.Append("You must select a valid Trigger Start date\r\n");
            if (chkActorStopTrigger.IsChecked.Value && dtpActorTriggerStop.SelectedDate == null) sb.Append("If selecting StopTrigger you must select a valid Trigger Stop date\r\n");
            if (txtActorTriggerAmount.Text == "") sb.Append("You must specify a trigger amount\r\n");
            else
            {
                Decimal d;
                if (!Decimal.TryParse(txtActorTriggerAmount.Text, out d)) sb.Append("Trigger amount is not a valid amount\r\n");
            }
            if (chkActorIsInterestAccrual.IsChecked.Value)
            {
                Decimal d;

                if (txtActorAccrualRate.Text == "") sb.Append("If selecting interest accrual you must enter an accrual rate\r\n");
                else {
                    if (!Decimal.TryParse(txtActorAccrualRate.Text, out d)) sb.Append("Accrual rate is not a valid amount\r\n");
                }
            }
      
            return sb.ToString();
        }

        private void FlagNewActor()
        {
            btnNewActor.IsEnabled = false;
            btnEditActor.IsEnabled = false;
            btnDeleteActor.IsEnabled = false;
        }

        private void UnflagNewActor()
        {
            btnNewActor.IsEnabled = true;
            btnEditActor.IsEnabled = true;
            btnDeleteActor.IsEnabled = true;
        }

        private void lstActors_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (lstActors.SelectedItem == null) return;

            Actor act = (Actor)lstActors.SelectedItem;

            txtActorName.Text = act.ActorName;
            chkActorDisabled.IsChecked = act.IsDisabled;
            ddlActorFundDest.SelectedValue = act.ActorFundPrimary;
            ddlActorFundFeeder.SelectedValue = act.ActorFundSecondary;
            chkActorHasFeeder.IsChecked = (act.ActorFundSecondary != ""); 
            ddlActorPeriodicity.SelectedItem = act.ActorPeriodicity.ToString();
            txtActorPeriodicityUnits.Text = act.ActorPeriodicityUnits.ToString();

            dtpActorTriggerStart.SelectedDate = act.TriggerDateInitial;
            if (act.TriggerDateEnd == DateTime.MaxValue)
            {
                chkActorStopTrigger.IsChecked = false;
                dtpActorTriggerStop.SelectedDate = DateTime.Now.Date;
            }
            else
            {
                dtpActorTriggerStop.SelectedDate = act.TriggerDateEnd;
                chkActorStopTrigger.IsChecked = true;
            }

            txtActorTriggerAmount.Text = act.TriggerAmount.ToString();

            chkActorIsLoanPayment.IsChecked = act.IsLoanPayment;
            chkActorIsInterestAccrual.IsChecked = act.IsCompoundInterestAccrual;
            txtActorAccrualRate.Text = act.InterestRate.ToString();

            LockActorDetails();
            UnflagNewActor();
        }

        private void btnEditActor_Click(object sender, RoutedEventArgs e)
        {
            UnlockActorDetails();
            txtActorName.IsEnabled = false;
        }

        private void btnNewActor_Click(object sender, RoutedEventArgs e)
        {
            ClearActorDetails();
            UnlockActorDetails();
            FlagNewActor();
        }

        private void btnSaveActor_Click(object sender, RoutedEventArgs e)
        {
            string errors = ValidateActorDetails();

            if (errors != "")
            {
                MessageBox.Show(errors, "Validation errors need to be fixed", MessageBoxButton.OK);
                return;
            }

            Actor actor;

            if (txtActorName.IsEnabled)
            {
                actor = new Actor();
                actor.ActorName = txtActorName.Text;
            }
            else actor = (Actor)lstActors.SelectedItem;

            actor.IsDisabled = chkActorDisabled.IsChecked.Value;
            
            Fund fund = (Fund) ddlActorFundDest.SelectedItem;
            actor.ActorFundPrimary = fund.Name;

            if (chkActorHasFeeder.IsChecked.Value)
            {
                fund = (Fund)ddlActorFundFeeder.SelectedItem;
                actor.ActorFundSecondary = fund.Name;
            }
            else actor.ActorFundSecondary = "";

            actor.ActorPeriodicity = (ActorPeriodicityEnum)Enum.Parse(typeof(ActorPeriodicityEnum), ddlActorPeriodicity.SelectedItem.ToString(), true);
            actor.ActorPeriodicityUnits = int.Parse(txtActorPeriodicityUnits.Text);

            actor.TriggerDateInitial = dtpActorTriggerStart.SelectedDate.Value;
            if (chkActorStopTrigger.IsChecked.Value) actor.TriggerDateEnd = dtpActorTriggerStop.SelectedDate.Value;
            else actor.TriggerDateEnd = DateTime.MaxValue;
            actor.TriggerAmount = Decimal.Parse(txtActorTriggerAmount.Text);

            actor.IsLoanPayment = chkActorIsLoanPayment.IsChecked.Value;
            if (chkActorIsInterestAccrual.IsChecked.Value)
            {
                actor.IsCompoundInterestAccrual = true;
                actor.CompoundInterestRate = Decimal.Parse(txtActorAccrualRate.Text);
            }
            else
            {
                actor.IsCompoundInterestAccrual = false;
                actor.CompoundInterestRate = 0.0m;
            }

            if (txtActorName.IsEnabled)
            {
                projector.Actors.Add(actor);
                RunProjection(true);
            }
            else RunProjection(false);

            LockActorDetails();
            UnflagNewActor();
        }

        private void btnDeleteActor_Click(object sender, RoutedEventArgs e)
        {
            if (lstActors.SelectedItem == null) return;
            Actor act = (Actor) lstActors.SelectedItem;

            if (MessageBox.Show("Are you sure you want to delete this actor?", "Confirm Actor Deletion", MessageBoxButton.OKCancel) != MessageBoxResult.OK) return;

            projector.Actors.Remove(act);

            RunProjection(true);
        }

        private void dtpActorTriggerStart_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
        {
            if (dtpActorTriggerStart.SelectedDate > dtpActorTriggerStop.SelectedDate) 
                dtpActorTriggerStop.SelectedDate = dtpActorTriggerStart.SelectedDate;
        }

        #endregion

        #region Historical Balances Tab Handlers

        private void LockHistBalDetails()
        {
            dtpHistBalDate.IsEnabled = false;
            txtHistBalAmount.IsEnabled = false;
            txtHistBalDesc.IsEnabled = false;
        }

        private void UnlockHistBalDetails() 
        {
            dtpHistBalDate.IsEnabled = true;
            txtHistBalAmount.IsEnabled = true;
            txtHistBalDesc.IsEnabled = true;
        }

        private void ClearHistBalDetails() 
        {
            dtpHistBalDate.SelectedDate = DateTime.Now.Date;
            txtHistBalAmount.Text = "";
            txtHistBalDesc.Text = "";
        }

        private string ValidateHistBalDetails() 
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            if (txtHistBalDesc.Text == "") sb.Append("Description is required\r\n");
            if (dtpHistBalDate.SelectedDate == null) sb.Append("Balance Date is required\r\n");
            if (txtHistBalAmount.Text != "")
            {
                Decimal d;

                if (!Decimal.TryParse(txtHistBalAmount.Text, out d)) sb.Append("Balance Amount is not a valid amount\r\n");
            }

            return sb.ToString();
        }

        private void RefreshHistoricalBals(Fund f)
        {
            lstHistoricalBalances.ItemsSource = null;
            lstHistoricalBalances.ItemsSource = f.HistoricalBalances;

            if (lstHistoricalBalances.Items.Count > 0) lstHistoricalBalances.SelectedIndex = 0;
        }

        private void ddlFundsHistBals_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            lstHistoricalBalances.ItemsSource = null; 

            if (ddlHistoricalBalsFunds.SelectedItem == null) return;

            Fund f = (Fund) ddlHistoricalBalsFunds.SelectedItem;

            RefreshHistoricalBals(f);
        }

        private void FlagNewHistBal()
        {
            btnHistBalNew.IsEnabled = false;
            btnHistBalEdit.IsEnabled = false;
            btnHistBalDelete.IsEnabled = false;
        }

        private void UnflagNewHistBal()
        {
            btnHistBalNew.IsEnabled = true;
            btnHistBalEdit.IsEnabled = true;
            btnHistBalDelete.IsEnabled = true;
        }

        private void lstHistoricalBalances_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (lstHistoricalBalances.SelectedItem == null) return;

            Balance b = (Balance)lstHistoricalBalances.SelectedItem;

            dtpHistBalDate.SelectedDate = b.BalanceDate;
            txtHistBalAmount.Text = b.Amount.ToString();
            txtHistBalDesc.Text = b.Description;
            UnflagNewHistBal();

            LockHistBalDetails();
        }

        private void btnHistBalNew_Click(object sender, RoutedEventArgs e)
        {
            ClearHistBalDetails();
            UnlockHistBalDetails();
            FlagNewHistBal();
        }

        private void btnHistBalEdit_Click(object sender, RoutedEventArgs e)
        {
            UnlockHistBalDetails();
        }

        private void btnHistBalSave_Click(object sender, RoutedEventArgs e)
        {
            string errors = ValidateHistBalDetails();

            if (errors != "")
            {
                MessageBox.Show(errors, "Validation errors need to be fixed", MessageBoxButton.OK);
                return;
            }

            if (ddlHistoricalBalsFunds.SelectedItem == null) return;
            Fund f = (Fund)ddlHistoricalBalsFunds.SelectedItem;

            Balance bal;

            if (btnHistBalNew.IsEnabled)
            {
                if (lstHistoricalBalances.SelectedItem == null) return;
                bal = (Balance)lstHistoricalBalances.SelectedItem;
                bal.BalanceDate = dtpHistBalDate.SelectedDate.Value;
                bal.Amount = Decimal.Parse(txtHistBalAmount.Text);
                bal.Description = txtHistBalDesc.Text;
            }
            else
            {
                bal = new Balance();
                bal.BalanceDate = dtpHistBalDate.SelectedDate.Value;
                bal.Amount = Decimal.Parse(txtHistBalAmount.Text);
                bal.Description = txtHistBalDesc.Text;

                if (!btnHistBalNew.IsEnabled) f.HistoricalBalances.Add(bal);
            }

            RefreshHistoricalBals(f);
            RunProjection(false);
        }

        private void btnHistBalDelete_Click(object sender, RoutedEventArgs e)
        {
            if (ddlHistoricalBalsFunds.SelectedItem == null) return;
            if (lstHistoricalBalances.SelectedItem == null) return;

            if (MessageBox.Show("Are you sure you want to delete this Historical Balance?", 
                                "Confirm Deletion", MessageBoxButton.OKCancel) != MessageBoxResult.OK) return;

            Fund f = (Fund)ddlHistoricalBalsFunds.SelectedItem;
            Balance bal = (Balance)lstHistoricalBalances.SelectedItem;

            f.HistoricalBalances.Remove(bal);

            RefreshHistoricalBals(f);
            RunProjection(false);
        }

        #endregion

        #region GraphingTabs Misc Handlers

        private void ddlFundDisbursal_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (ddlFundDisbursal.SelectedItem == null) return;

            Fund f = (Fund)ddlFundDisbursal.SelectedItem;
            if (f.FundClass == FundClassEnum.Savings) chkFundDisburseReversed.IsChecked = true;
            else chkFundDisburseReversed.IsChecked = false;

            RenderFundDisbursalGraph();
        }

        private void chkFundDisburseReversed_Click(object sender, RoutedEventArgs e)
        {
            RenderFundDisbursalGraph();
        }

        private void ddlActivityFunds_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            RefreshActivityTab();
        }

        private void chkStackedDebtRelative_Click(object sender, RoutedEventArgs e)
        {
            DateTime StartDate = dtpStartDate.SelectedDate.Value, EndDate = dtpEndDate.SelectedDate.Value;

            RenderStackedDebtGraph(StartDate, EndDate);
        }

        #endregion

    }
}